package main

import (
	"errors"
	"time"

	mgo "gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
)

type DBOperation struct {
	session *mgo.Session
	dbName  string
	cfg     *Config
}

const (
	CollectionRawDatas               string = "rawdatas"
	CollectionRawDatasLast           string = "rawdataslast"
	CollectionReceivers              string = "receivers"
	CollectionSensors                string = "sensors"
	CollectionTires                  string = "tires"
	CollectionIpBlackLists           string = "ipblacklists"
	CollectionIllegalAccesses        string = "illegalaccesses"
	CollectionLastStatuses           string = "laststatuses"
	CollectionIpWhiteLists           string = "ipwhitelists"
	CollectionOrganizationProperties string = "organizationproperties"
	CollectionCommands               string = "commands"
	CollectionRelations              string = "relations"
)

const (
	RawDataStatNew   int = 0
	RawDataStatRead  int = 1
	RawDataStatDone  int = 2
	RawDataStatError int = 3
)

const (
	ReceiverStatusNone    int = 0
	ReceiverStatusOnline  int = 1
	ReceiverStatusOffline int = -1
)

const (
	CommandStatusNew       int = 0
	CommandStatusSent      int = 1
	CommandStatusDone      int = 2
	CommandStatusDuplicate int = 3
	CommandStatusInvalid   int = -1
)

func NewDBOperation(cfg *Config) (*DBOperation, error) {
	zLog(nil, "数据库连接::", cfg.DBUrl, " 开始")
	session, err := mgo.Dial(cfg.DBUrl)
	if err != nil {
		zLog(nil, "数据库连接失败::", err.Error())
		return nil, err
	}
	if cfg.DBUser != "" && cfg.DBPwd != "" {
		err = session.Login(&mgo.Credential{
			Username: cfg.DBUser,
			Password: cfg.DBPwd,
		})
		if err != nil {
			zLog(nil, "数据库登录失败::", err.Error())
			return nil, err
		}
	}
	session.SetPoolLimit(100)

	dbo := DBOperation{session, cfg.DBName, cfg}

	return &dbo, nil
}

func (p *DBOperation) Close() {
	p.session.Close()
}

type RowDataT struct {
	ID   bson.ObjectId `bson:"_id"`
	Imei string        `bson:"imei"`
	Dt   string        `bson:"dt"`
	T    int64         `bson:"t"`
	Data string        `bson:"data"`
	Stat int           `bson:"stat"`
}

func (p *DBOperation) rawDataCollectionName() string {
	ts := time.Now().Format("0601")
	if ts < "2112" {
		ts = ""
	}

	return ts
}

func (p *DBOperation) SaveRawData(receiverid string, dataType string, data string) bson.ObjectId {
	s := p.session.Copy()
	defer s.Close()
	c := s.DB(p.dbName).C(CollectionRawDatas + p.rawDataCollectionName())
	d := RowDataT{ID: bson.NewObjectId(), Imei: receiverid, T: time.Now().Unix(), Data: data, Dt: dataType, Stat: RawDataStatNew}

	if dataType == "0900" || dataType == "0200" {
		cLast := s.DB(p.dbName).C(CollectionRawDatasLast)
		cLast.Insert(d)
	}

	err := c.Insert(d)
	if err == nil {
		return d.ID
	} else {
		return ""
	}
}

func (p *DBOperation) SaveRawDataReply(oid bson.ObjectId, reply string, err error) error {
	if reply == "" && err == nil {
		return nil
	}

	s := p.session.Copy()
	defer s.Close()
	c := s.DB(p.dbName).C(CollectionRawDatas + p.rawDataCollectionName())
	if err != nil {
		return c.Update(bson.M{"_id": oid}, bson.M{"$set": bson.M{"rdata": reply, "stat": RawDataStatError, "err": err.Error()}})
	} else {
		return c.Update(bson.M{"_id": oid}, bson.M{"$set": bson.M{"rdata": reply}})
	}
}

type RegisterT struct {
	Type int    `bson:"type"`
	IMEI string `bson:"imei"`
}

func (p *DBOperation) Register(receiverid, imei string) error {
	s := p.session.Copy()
	defer s.Close()

	c := s.DB(p.dbName).C(CollectionReceivers)
	var r RegisterT
	err := c.Find(bson.M{"rid": receiverid}).Select(bson.M{"type": 1, "imei": 1}).One(&r)
	if err != nil {
		err = errors.New("未找到此设备：" + receiverid)
	} else {

		if imei != r.IMEI {
			c.Update(bson.M{"rid": receiverid}, bson.M{"$set": bson.M{"imei": imei}})
		}

		c = s.DB(p.dbName).C(CollectionLastStatuses)
		c.Upsert(bson.M{"rid": receiverid}, bson.M{"$set": bson.M{"type": r.Type}})
	}
	return err
}

func (p *DBOperation) SaveAuthCode(receiverid string, code string) {
	s := p.session.Copy()
	defer s.Close()

	c := s.DB(p.dbName).C(CollectionReceivers)
	c.Update(bson.M{"rid": receiverid}, bson.M{"$set": bson.M{"authcode": code}})
}

func (p *DBOperation) GetAuthCode(receiverid string) string {
	s := p.session.Copy()
	defer s.Close()

	c := s.DB(p.dbName).C(CollectionReceivers)
	var res map[string]string

	err := c.Find(bson.M{"rid": receiverid}).Select(bson.M{"_id": 0, "authcode": 1}).One(&res)
	if err != nil {
		return ""
	} else {
		return res["authcode"]
	}
}

type ReceiverPropertyT struct {
	ICCID               string `bson:"iccid"`
	HardwareVersion     string `bson:"hver"`
	FirmwareProjectCode string `bson:"pcode"`
	FirmwareVersion     string `bson:"fver"`
	TMPSModuleVersion   string `bson:"tver"`
}

func (p *DBOperation) SaveReceiverProperty(rid string, property *ReceiverPropertyT) {
	s := p.session.Copy()
	defer s.Close()

	c := s.DB(p.dbName).C(CollectionReceivers)
	var oldP ReceiverPropertyT
	err := c.Find(bson.M{"rid": rid}).Select(bson.M{"_id": 0, "iccid": 1, "hver": 1, "pcode": 1, "fver": 1, "tver": 1}).One(&oldP)
	if err == nil {
		if property.ICCID != oldP.ICCID || property.HardwareVersion != oldP.HardwareVersion ||
			property.FirmwareProjectCode != oldP.FirmwareProjectCode || property.FirmwareVersion != oldP.FirmwareVersion ||
			property.TMPSModuleVersion != oldP.TMPSModuleVersion {
			c.Update(bson.M{"rid": rid}, bson.M{"$set": bson.M{"iccid": property.ICCID, "hver": property.HardwareVersion,
				"pcode": property.FirmwareProjectCode, "fver": property.FirmwareVersion, "tver": property.TMPSModuleVersion}})
		}
	}
}

type ReceiverParamenterT struct {
	ServerIP             string `bson:"serverip"`
	ServerPort           int    `bson:"serverport"`
	GPSInterval          int    `bson:"gpsinterval"`
	TirePressureInterval int    `bson:"preinterval"`
	DeviceSleep          int    `bson:"devicesleep"`
}

func (p *DBOperation) SaveReceiverParameter(rid string, parameter *ReceiverParamenterT) {
	s := p.session.Copy()
	defer s.Close()
	c := s.DB(p.dbName).C(CollectionReceivers)

	var oldP ReceiverParamenterT
	err := c.Find(bson.M{"rid": rid}).Select(bson.M{"_id": 0, "serverip": 1, "serverport": 1, "gpsinterval": 1,
		"preinterval": 1, "devicesleep": 1}).
		One(&oldP)
	if err == nil {

		if parameter.ServerIP != oldP.ServerIP || parameter.ServerPort != oldP.ServerPort ||
			parameter.GPSInterval != oldP.GPSInterval || parameter.TirePressureInterval != oldP.TirePressureInterval ||
			parameter.DeviceSleep != oldP.DeviceSleep {
			c.Update(bson.M{"rid": rid}, bson.M{"$set": bson.M{"serverip": parameter.ServerIP, "serverport": parameter.ServerPort,
				"gpsinterval": parameter.GPSInterval, "preinterval": parameter.TirePressureInterval,
				"devicesleep": parameter.DeviceSleep}})
		}
	}
}

func (p *DBOperation) GetIpBlacklists() []ImportData {
	var ids []ImportData

	s := p.session.Copy()
	defer s.Close()
	c := s.DB(p.dbName).C(CollectionIpBlackLists)
	err := c.Find(nil).Select(bson.M{"ip": 1, "count": 1, "_id": 0}).All(&ids)
	if err != nil {

		ids = make([]ImportData, 0)
	}
	return ids
}

type IllegalAccesseT struct {
	Ip         string `bson:"ip"`
	AccessTime int64  `bson:"at"`
	ReceiverId string `bson:"rid"`
}

type IpBlacklistT struct {
	Ip       string `bson:"ip"`
	AddTime  int64  `bson:"at"`
	Count    int    `bson:"count"`
	LastTime int64  `bson:"lt"`
}

func (p *DBOperation) RecordIllegal(ip string, count int, id string) {
	s := p.session.Copy()
	defer s.Close()

	c := s.DB(p.dbName).C(CollectionIllegalAccesses)
	ia := IllegalAccesseT{Ip: ip, AccessTime: time.Now().Unix(), ReceiverId: id}
	c.Insert(ia)

	c = s.DB(p.dbName).C(CollectionIpBlackLists)
	var res interface{}
	err := c.Find(bson.M{"ip": ip}).Select(bson.M{"at": 1, "_id": 0}).One(&res)
	if err != nil {

		il := IpBlacklistT{Ip: ip, AddTime: time.Now().Unix(), Count: count, LastTime: time.Now().Unix()}
		c.Insert(il)
	} else {

		c.Update(bson.M{"ip": ip}, bson.M{"$set": bson.M{"count": count, "lt": time.Now().Unix()}})
	}
}

func (p *DBOperation) SetReceiverStatus(receiverId string, status int) {
	if receiverId == "" {
		return
	}

	s := p.session.Copy()
	defer s.Close()

	c := s.DB(p.dbName).C(CollectionLastStatuses)
	c.UpdateAll(bson.M{"rid": receiverId, "status": bson.M{"$ne": -2}}, bson.M{"$set": bson.M{"status": status}})

	c = s.DB(p.dbName).C(CollectionReceivers)
	c.UpdateAll(bson.M{"rid": receiverId, "status": bson.M{"$ne": -2}}, bson.M{"$set": bson.M{"status": status}})
}

func (p *DBOperation) SetLastCommunicateTime(receiverId string) {
	if receiverId == "" {
		return
	}

	s := p.session.Copy()
	defer s.Close()

	c := s.DB(p.dbName).C(CollectionLastStatuses)
	c.UpdateAll(bson.M{"rid": receiverId}, bson.M{"$set": bson.M{"lt": time.Now().Unix()}})

	c = s.DB(p.dbName).C(CollectionReceivers)
	c.UpdateAll(bson.M{"rid": receiverId}, bson.M{"$set": bson.M{"lt": time.Now().Unix()}})
}

func (p *DBOperation) LoadIPWHiteLists() []string {
	s := p.session.Copy()
	defer s.Close()

	c := s.DB(p.dbName).C(CollectionIpWhiteLists)
	var res []map[string]string
	var ret []string

	c.Find(nil).All(&res)
	if len(res) > 0 {
		ret = make([]string, 0, len(res))
		for _, v := range res {
			ret = append(ret, v["ip"])
		}
	}

	return ret
}

func (p *DBOperation) IPWhiteListsAdd(ip string) {
	s := p.session.Copy()
	defer s.Close()

	c := s.DB(p.dbName).C(CollectionIpWhiteLists)
	if c.Find(bson.M{"ip": ip}).One(nil) != nil {
		c.Insert(bson.M{"ip": ip})
	}
}

func (p *DBOperation) IPWhiteListsRemove(ip string) {
	s := p.session.Copy()
	defer s.Close()

	c := s.DB(p.dbName).C(CollectionIpWhiteLists)
	c.Remove(bson.M{"ip": ip})
}

func (p *DBOperation) CheckSerialNumber(cid int, sn string) bool {
	s := p.session.Copy()
	defer s.Close()

	c := s.DB(p.dbName).C(CollectionOrganizationProperties)
	return c.Find(bson.M{"cid": cid, "rid": sn}).Select(bson.M{"_id": 1}).One(nil) == nil
}

func (p *DBOperation) SaveCommand(rid string, serialNo uint16, message string, command string) bson.ObjectId {
	s := p.session.Copy()
	defer s.Close()

	var stat int = CommandStatusNew
	c := s.DB(p.dbName).C(CollectionCommands)

	err := c.Find(bson.M{"rid": rid, "command": command, "stat": CommandStatusNew}).Select(bson.M{"_id": 1}).One(nil)
	if err == nil {
		stat = CommandStatusDuplicate
	}
	oid := bson.NewObjectId()
	c.Insert(bson.M{"_id": oid, "rid": rid, "sn": serialNo, "msg": message, "command": command, "stat": stat,
		"at": time.Now().Unix(), "repeat": 0})

	return oid
}

func (p *DBOperation) SetCommandStatus(oid bson.ObjectId, status int) {
	s := p.session.Copy()
	defer s.Close()

	c := s.DB(p.dbName).C(CollectionCommands)
	t := time.Now().Unix()
	if status == CommandStatusSent || status == CommandStatusDone {

		c.Update(bson.M{"_id": oid}, bson.M{"$set": bson.M{"stat": status, "st": t}})

	} else {

		c.Update(bson.M{"_id": oid}, bson.M{"$set": bson.M{"stat": status}})
	}
}

func (p *DBOperation) IncreaseCommandRepeatTimes(oid bson.ObjectId) {
	s := p.session.Copy()
	defer s.Close()

	c := s.DB(p.dbName).C(CollectionCommands)
	c.Update(bson.M{"_id": oid}, bson.M{"$inc": bson.M{"repeat": 1}})
}

func (p *DBOperation) SetCommandStatusAndRepeatTimes(oid bson.ObjectId, status int) {
	s := p.session.Copy()
	defer s.Close()

	c := s.DB(p.dbName).C(CollectionCommands)
	c.Update(bson.M{"_id": oid}, bson.M{"$set": bson.M{"stat": status}, "$inc": bson.M{"repeat": 1}})
}

type NewCommandT struct {
	Oid bson.ObjectId `bson:"_id"`
	Msg string        `bson:"msg"`
}

func (p *DBOperation) GetANewCommand(rid string) (bson.ObjectId, string) {
	s := p.session.Copy()
	defer s.Close()

	c := s.DB(p.dbName).C(CollectionCommands)
	var res NewCommandT

	err := c.Find(bson.M{"rid": rid, "stat": CommandStatusNew}).
		Select(bson.M{"_id": 1, "msg": 1}).Sort("_id").Limit(1).One(&res)
	if err != nil {
		return "", ""
	}
	return res.Oid, res.Msg
}

type TireRelationT struct {
	Oid    bson.ObjectId `bson:"_id"`
	Sid    string        `bson:"sid"`
	TireNo string        `bson:"tno"`
	CardId int           `bson:"cardid"`
	SN     int           `bson:"sn"`
}

func (p *DBOperation) GetRelations(rid string) []TireRelationT {
	s := p.session.Copy()
	defer s.Close()

	c := s.DB(p.dbName).C(CollectionRelations)
	var res []TireRelationT

	c.Find(bson.M{"rid": rid, "sid": bson.M{"$exists": 1}}).Select(bson.M{"_id": 1, "sid": 1, "cardid": 1, "sn": 1, "tno": 1}).
		Sort("cardid,sn").All(&res)
	return res
}

func (p *DBOperation) ReplaceSensorId(oid bson.ObjectId, sid string, tno string) {
	s := p.session.Copy()
	defer s.Close()

	c := s.DB(p.dbName).C(CollectionRelations)
	c.Update(bson.M{"_id": oid}, bson.M{"$set": bson.M{"sid": sid, "tno": tno}})
}

type NewServerT struct {
	IP   string `bson:"newserverip"`
	Port int    `bson:"newserverport"`
}

func (p *DBOperation) GetReceiverNewServer(rid string) (string, int, error) {
	s := p.session.Copy()
	defer s.Close()

	c := s.DB(p.dbName).C(CollectionReceivers)
	var res NewServerT

	err := c.Find(bson.M{"rid": rid}).Select(bson.M{"_id": 0, "newserverip": 1, "newserverport": 1}).One(&res)
	if err != nil {
		return "", 0, err
	}
	return res.IP, res.Port, nil
}

func (p *DBOperation) TerminalCommonAnswer(rid string, msgType string, sn int, result int) {
	s := p.session.Copy()
	defer s.Close()

	c := s.DB(p.dbName).C(CollectionCommands)
	c.Update(bson.M{"rid": rid, "type": msgType, "sn": sn},
		bson.M{"$set": bson.M{"result": result, "stat": CommandStatusDone, "rt": time.Now().Unix()}})
}

/*

func (p *DBOperation) SaveReceiverThreshold(rid string, pressureThreshold [5][2]float32, temperatureThreshold int) {
	s := p.session.Copy()
	defer s.Close()

	c := s.DB(p.dbName).C(CollectionReceivers)
	c.Update(bson.M{"rid": rid}, bson.M{"$set": bson.M{"threshold.pres": pressureThreshold, "threshold.temp": temperatureThreshold}})
}
*/
